//-----------------------------------------------------------------------
// <copyright file="Tuple.cs" company="tuliprules.org">
//     Copyright (c) 2010 tuplelister, tuliprules.org.
// </copyright>
// <summary>This is the Tuple class.</summary>
//-----------------------------------------------------------------------

namespace Tulip.Engine.Tuples
{
    using System;
    using System.Collections.Generic;
    using EngineOperation;
    using Exceptions;

    /// <summary>
    /// Base class for all tuples.
    /// </summary>
    [Serializable]
    public abstract class Tuple
    {
        /// <summary>
        /// The generational value for this tuple.   This value is used during quantifiaction
        /// to control refraction (i.e., to prevent the same production from repeatedly being
        /// activated on the agenda.
        /// </summary>
        public int Generation;

        /// <summary>
        /// The name of the production (rule).
        /// </summary>
        public string ProductionName;

        /// <summary>
        /// The salience (priority) of the production.
        /// </summary>
        public int Salience;

        /// <summary>
        /// The specificity (complexity) of the production.
        /// </summary>
        public int Specificity;

        /// <summary>
        /// The position of the production rule within the rule set.
        /// </summary>
        public int Position;

        /// <summary>
        /// The timestamp of the tuple.
        /// </summary>
        public long Timestamp;

        /// <summary>
        /// The next tuple on the agenda.
        /// </summary>
        public Tuple AgendaNext;

        /// <summary>
        /// The previous tuple on the agenda.
        /// </summary>
        public Tuple AgendaPrevious;

        /// <summary>
        /// The previous tuple in the pool.
        /// </summary>
        public Tuple PoolPrevious;

        /// <summary>
        /// Indicates if Identifiers are being traced.
        /// </summary>
        public bool TraceIDs;

        /// <summary>
        /// The index of the tuple instance in the agenda allowing fast removal.
        /// </summary>
        internal int AgendaTimestamp = -1;

#pragma warning disable 1587
#if FULL_ASYMMETRIC_RETRACT
        /// <summary>
        /// List of subsumed tuples.
        /// </summary>
        internal SubsumerEntry SubsumerTuplesHead;

        /// <summary>
        /// List of subsumed tuples.
        /// </summary>
        internal SubsumerEntry SubsumerTuplesTail;

        /// <summary>
        /// Array of subsumer entries
        /// </summary>
        internal SubsumerEntry[] SubsumerEntries = new SubsumerEntry[2];

        /// <summary>
        /// Subsumer entry record
        /// </summary>
        internal struct SubsumerEntry
        {
            /// <summary>
            /// Previous tuple in linked list.
            /// </summary>
            public Tuple PrevTuple;

            /// <summary>
            /// Index of previous tuple in lonked list.
            /// </summary>
            public int PrevIndex;

            /// <summary>
            /// Next tuple in linked list.
            /// </summary>
            public Tuple NextTuple;

            /// <summary>
            /// Index of next tuple in linked list.
            /// </summary>
            public int NextIndex;
        }
#else
        /// <summary>
        /// List of subsumed tuples.
        /// </summary>
        internal Queue<Tuple> SubsumerTuples = new Queue<Tuple>();
#endif
#pragma warning restore 1587

        /// <summary>
        /// Indicates if this tuple instance is currently pooled.
        /// </summary>
        internal bool IsPooled;

        /// <summary>
        /// A list of identifier numbers for the facts.   The identifiers are held in a sorted list to 
        /// facilitate their use as timestamps.
        /// </summary>
        internal List<long> Identifiers;

        /* These members might alternatively be implmented as methods and properties in
         * a 'mix-in' interface. However, it is nore optimal to declare them as public fields
         * and methods of the tuple class.    They are used specifically for tuples which
         * are added to the agenda as productions.
         * */
        #region Production Members

        /// <summary>
        /// Sets the 'next' tuple in the linked list of tuples held within a tuple list monad.
        /// </summary>
        internal Tuple MonadTupleNext;

        /// <summary>
        /// Sets the 'previous' tuple in the linked list of tuples held within a tuple list monad.
        /// </summary>
        internal Tuple MonadTuplePrevious;

        /// <summary>
        /// List of tuple lists to which this tuple belongs.
        /// </summary>
        private readonly Queue<TupleLinkedList> tupleLists = new Queue<TupleLinkedList>();

        /// <summary>
        /// Perfoms the actions of the production.
        /// </summary>
        /// <param name="productionEngineOps">Collections of engine operations.</param>
        /// <returns>A collections of engine operations.</returns>
        public virtual ProductionEngineOps DoActions(ProductionEngineOps productionEngineOps)
        {
            return productionEngineOps;
        }

        #endregion

        /// <summary>
        /// Returns the 'next' tuple in the linked list of tuples.
        /// </summary>
        /// <param name="indexerId">Tuple linked list indexer identifier.</param>
        /// <returns>The next tuple.</returns>
        public virtual Tuple GetNextTuple(int indexerId)
        {
            return null;
        }

        /// <summary>
        /// Returns the 'previous' tuple in the linked list of tuples.
        /// </summary>
        /// <param name="indexerId">Tuple linked list indexer identifier.</param>
        /// <returns>The previous tuple.</returns>
        public virtual Tuple GetPreviousTuple(int indexerId)
        {
            return null;
        }

        /// <summary>
        /// Sets the 'next' tuple in the linked list of tuples.
        /// </summary>
        /// <param name="indexerId">Tuple linked list indexer identifier.</param>
        /// <param name="tuple">Tuple to be added to linked list.</param>
        public virtual void SetNextTuple(int indexerId, Tuple tuple)
        {
        }

        /// <summary>
        /// Sets the 'previous' tuple in the linked list of tuples.
        /// </summary>
        /// <param name="indexerId">Tuple linked list indexer identifier.</param>
        /// <param name="tuple">Tuple to be added to linked list.</param>
        public virtual void SetPreviousTuple(int indexerId, Tuple tuple)
        {
        }

#pragma warning disable 1587
#if FULL_ASYMMETRIC_RETRACT
        /// <summary>
        /// Registers a tuple with this tuple.  This tuple subsumes the
        /// registered tuple.
        /// </summary>
        /// <param name="subsumedTuple">Tuple to be subsumed.</param>
        /// <param name="index">Index used to select subsumer entry.</param>
        public void RegisterSubsumerTuple(Tuple subsumerTuple, int index)
        {
            int idx = index - 1;

            if (SubsumerTuplesHead.NextIndex == 0)
            {
                // The head has not been set, so set to this tuple...

                try
                {
                    subsumerTuple.SubsumerEntries[idx].PrevTuple = this;
                    subsumerTuple.SubsumerEntries[idx].PrevIndex = 0;
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debugger.Break();
                }

                SubsumerTuplesHead.NextTuple = subsumerTuple;
                SubsumerTuplesHead.NextIndex = index;
                SubsumerTuplesTail = SubsumerTuplesHead;
            }
            else
            {
                try
                {
                    subsumerTuple.SubsumerEntries[idx].PrevTuple = SubsumerTuplesTail.NextTuple;
                    subsumerTuple.SubsumerEntries[idx].PrevIndex = SubsumerTuplesTail.NextIndex;
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debugger.Break();
                }

                int nextIdx = SubsumerTuplesTail.NextIndex - 1;
                SubsumerTuplesTail.NextTuple.SubsumerEntries[nextIdx].NextTuple = subsumerTuple;
                SubsumerTuplesTail.NextTuple.SubsumerEntries[nextIdx].NextIndex = index;
                SubsumerTuplesTail = SubsumerTuplesTail.NextTuple.SubsumerEntries[nextIdx];
            }

            for (int entIdx = 0; entIdx < subsumerTuple.SubsumerEntries.Length; entIdx++)
            {
                subsumerTuple.SubsumerEntries[entIdx].NextIndex = 0;
            }
        }
#else
        /// <summary>
        /// Registers a tuple with this tuple.  This tuple subsumes the
        /// registered tuple.
        /// </summary>
        /// <param name="subsumedTuple">Tuple to be subsumed.</param>
        public void RegisterSubsumerTuple(Tuple subsumedTuple)
        {
            this.SubsumerTuples.Enqueue(subsumedTuple);
        }
#endif
#pragma warning restore 1587

        /// <summary>
        /// Clear remaining subsumed tuples and tuple lists.
        /// </summary>
        public virtual void Clear()
        {
            // Remove the tuple from all registered tuple lists.
            while (this.tupleLists.Count > 0)
            {
                var tupleList = this.tupleLists.Dequeue();
                tupleList.Remove(this);
            }

            if (this.TraceIDs)
            {
                this.Identifiers.Clear();
            }

            this.Generation = 0;
            this.AgendaTimestamp = -1;
        }

        /// <summary>
        /// Links this tuple to a linked list of tuples.
        /// </summary>
        /// <param name="tupleList">The linked list of tuples.</param>
        internal void Link(TupleLinkedList tupleList)
        {
            var indexerId = tupleList.IndexerId;
            var endTuple = tupleList.EndTuple;

            this.SetPreviousTuple(indexerId, endTuple);

            if (endTuple != null)
            {
                endTuple.SetNextTuple(indexerId, this);
            }

            this.SetNextTuple(indexerId, null);
            //// SetCountAtInsertion(indexerId, tupleList.Count + 1);
        }

        /// <summary>
        /// Unlinks this tuple from a linked list of tuples.
        /// </summary>
        /// <param name="tupleList">The linked list of tuples.</param>
        /// <returns>The next tuple in the linked list.</returns>
        internal Tuple Unlink(TupleLinkedList tupleList)
        {
            var indexerId = tupleList.IndexerId;
            var nextMatchedTuple = this.GetNextTuple(indexerId);
            var previousMatchedTuple = this.GetPreviousTuple(indexerId);

            if (nextMatchedTuple != null)
            {
                nextMatchedTuple.ReplaceNext(indexerId, previousMatchedTuple);
            }

            if (previousMatchedTuple == null)
            {
                tupleList.StartTuple = nextMatchedTuple;
            }
            else
            {
                previousMatchedTuple.ReplacePrevious(indexerId, nextMatchedTuple);
            }

            this.SetNextTuple(indexerId, null);
            this.SetPreviousTuple(indexerId, null);
            //// SetCountAtInsertion(indexerId, 0);

            return previousMatchedTuple;
        }

        /// <summary>
        /// Returns the previous tuple from the linked list of tuples.
        /// </summary>
        /// <param name="tupleList">The linked list of tuples.</param>
        /// <returns>The previous tuple.</returns>
        internal Tuple GetNextTuple(TupleLinkedList tupleList)
        {
            return this.GetNextTuple(tupleList.IndexerId);
        }

        /// <summary>
        /// Copies all id values from a tuple.
        /// </summary>
        /// <param name="tuple">The source tuple.</param>
        internal void CopyIdValues(Tuple tuple)
        {
            for (var idx = 0; idx < tuple.Identifiers.Count; idx++)
            {
                this.Identifiers.Add(tuple.Identifiers[idx]);
            }

            this.Identifiers.Sort();
        }

        /// <summary>
        /// Registers a tuple list with this tuple.
        /// </summary>
        /// <param name="tupleList">Tuple list to be registered.</param>
        internal void RegisterTupleList(TupleLinkedList tupleList)
        {
            if (tupleList == null)
            {
                TupleStoreExceptionHelper.ThrowTupleStoreExRegNullBlist();
                return;
            }

            this.tupleLists.Enqueue(tupleList);
        }

        /// <summary>
        /// Replaces the current 'next' tuple with the given tuple.
        /// </summary>
        /// <param name="indexerId">The index id of the tuple list.</param>
        /// <param name="nextMatchedTuple">The tuple which will replace the 'next' tuple.</param>
        private void ReplaceNext(int indexerId, Tuple nextMatchedTuple)
        {
            this.SetNextTuple(indexerId, nextMatchedTuple);
        }

        /// <summary>
        /// Replaces the current 'previous' tuple with the given tuple.
        /// </summary>
        /// <param name="indexerId">The index id of the tuple list.</param>
        /// <param name="previousMatchedTuple">The tuple which will replace the 'previous' tuple.</param>
        private void ReplacePrevious(int indexerId, Tuple previousMatchedTuple)
        {
            this.SetPreviousTuple(indexerId, previousMatchedTuple);
        }
    }
}